package com.examlpe.lotterysystem.service.Impl;
import com.examlpe.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import java.util.Date;
import java.security.Key;
import java.util.*;

import com.examlpe.lotterysystem.common.utils.RedisUtil;
import com.examlpe.lotterysystem.controller.param.ShowWiningRrecordsParam;
import com.examlpe.lotterysystem.dao.dataobject.*;

import com.examlpe.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.examlpe.lotterysystem.common.exception.ServiceException;
import com.examlpe.lotterysystem.common.utils.JacksonUtil;
import com.examlpe.lotterysystem.controller.param.DrawPrizeParam;
import com.examlpe.lotterysystem.dao.mapper.*;
import com.examlpe.lotterysystem.service.DrawPrizeService;
import com.examlpe.lotterysystem.service.dto.WiningRecordsDTO;
import com.examlpe.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.examlpe.lotterysystem.service.enums.ActivityStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.stream.Collectors;

import static com.examlpe.lotterysystem.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.examlpe.lotterysystem.common.config.DirectRabbitConfig.ROUTING;

@Slf4j
@Service
public class DrawPrizeServiceImpl implements DrawPrizeService {
    private final Long WINNING_RECORDS_TIMEOUT = 60*60*24*2L;
    private final String WINNING_RECOREDS_PREFIX = "WINNING_RECOREDS_";

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private PrizeMapper prizeMapper;
    
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private WinningRecordMapper winningRecordMapper;
    
    
    @Override
    public void drawPrize(DrawPrizeParam param) {
        Map<String,String> map = new HashMap<>();
        map.put("messageId",String.valueOf(UUID.randomUUID()));
        map.put("messageData", JacksonUtil.writeValueAsString(param));
        //发消息:发给那个交换机,绑定的key，消息体
        rabbitTemplate.convertAndSend(EXCHANGE_NAME,ROUTING,map);
        log.info("mq消息发送成功: map = {}",JacksonUtil.writeValueAsString(map));
    }

    @Override
    public Boolean checkDrawPrizeParam(DrawPrizeParam param) {
        ActivityDO activityDo = activityMapper.selectActivityById(param.getActivityId());
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectADId(
                param.getActivityId(),param.getPrizeId());
        //活动是否存在,奖品是否存在
        if(null == activityDo || null == activityPrizeDO) {
            log.info("校验抽奖请求失败,失败原因：{}",ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY.getMsg());
            return false;
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY);
        }
        //活动是否有效
        if(activityDo.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
            log.info("校验抽奖请求失败,失败原因：{}",ServiceErrorCodeConstants.ACTIVITY_COMPLETED);
            return false;
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_COMPLETED);
        }
        //奖品是否有效
        if(activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())) {
            log.info("校验抽奖请求失败,失败原因：{}",ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED);
            return false;
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED);
        }
        //中奖者人数是否和设置奖品数量一致
        if(activityPrizeDO.getPrizeAmount() != param.getWinnerList().size()) {
            log.info("校验抽奖请求失败,失败原因：{}",ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR);
            return false;
            //throw new ServiceException(ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR);
        }
        return true;
    }

    @Override
    public List<WinningRecordDO> saveWinnerRecords(DrawPrizeParam param) {
        //查询相关信息：活动，人员，奖品，活动关联奖品表
        ActivityDO activityDO = activityMapper.selectActivityById(param.getActivityId());
        List<UserDO> userDOList = userMapper.batchSelectbyIds(
                param.getWinnerList()
                        .stream()
                        .map(DrawPrizeParam.Winner::getUserId)
                        .collect(Collectors.toList()));
        PrizeDO prizeDO = prizeMapper.selectById(param.getPrizeId());
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectADId(param.getActivityId(),param.getPrizeId());
        //构造中奖者记录
        List<WinningRecordDO> winningRecordDOList = userDOList.stream().map(userDO -> {
            WinningRecordDO winningRecordDO = new WinningRecordDO();
            winningRecordDO.setActivityId(activityDO.getId());
            winningRecordDO.setActivityName(activityDO.getActivityName());
            winningRecordDO.setPrizeId(prizeDO.getId());
            winningRecordDO.setPrizeName(prizeDO.getName());
            winningRecordDO.setPrizeTier(activityPrizeDO.getPrizeTiers());
            winningRecordDO.setWinnerId(userDO.getId());
            winningRecordDO.setWinnerName(userDO.getUserName());
            winningRecordDO.setWinnerEmail(userDO.getEmail());
            winningRecordDO.setWinnerPhoneNumber(userDO.getPhoneNumber());
            winningRecordDO.setWinningTime(param.getWinningTime());
            return winningRecordDO;
        }).collect(Collectors.toList());
        winningRecordMapper.batchInsert(winningRecordDOList);
        //缓存中奖者记录
        //1.缓存奖品维度中奖记录（WinningRecord_activityId_prizeId,winningRecordDOList)
        cacheWinningRecords(param.getActivityId()+"_"+ param.getPrizeId(),winningRecordDOList,WINNING_RECORDS_TIMEOUT);
        //2.缓存活动维度中奖记录(WinningRecord_activvityId,winningRecordDOList)
        //已完成的活动可以进行缓存
        if(activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
            List<WinningRecordDO> allList = winningRecordMapper.selectByActivityId(activityDO.getId());
            cacheWinningRecords(String.valueOf(activityDO.getId()),allList,WINNING_RECORDS_TIMEOUT);
        }
        return winningRecordDOList;
    }

    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        if(activityId == null) {
            log.warn("要删除的中奖记录相关的活动id为空");
            return;
        }
        //删除数据表
        winningRecordMapper.deleteRecords(activityId,prizeId);

        //删除缓存(奖品维度，活动维度)
        if(prizeId == null) {
            deleteWinningRecords(activityId+"_"+prizeId);
        }
        //无论是否传递了prizeId,都需要删除活动维度中奖记录缓存
        deleteWinningRecords(String.valueOf(activityId));

    }

    @Override
    public List<WiningRecordsDTO> getRecords(ShowWiningRrecordsParam param) {
        //从redis中查询:奖品，活动
        String key = null == param.getPrizeId()
                ? String.valueOf(param.getActivityId())
                : param.getActivityId() + "_" + param.getPrizeId();
        List<WinningRecordDO> winningRecordDOList = getWinningRrecords(key);
        if(!CollectionUtils.isEmpty(winningRecordDOList)) {
            return convertToWiningRecordsDTO(winningRecordDOList) ;
        }
        //如果redis中不存在，查库
        winningRecordDOList = winningRecordMapper.selectByActivityIdOrPrizeId(
                param.getActivityId(),param.getPrizeId());

        if(CollectionUtils.isEmpty(winningRecordDOList)) {
            log.info("查询的中奖记录为空:{}",
                    JacksonUtil.writeValueAsString(param));
            return Arrays.asList();
        }
        //存放记录到redis中
        cacheWinningRecords(key,winningRecordDOList,WINNING_RECORDS_TIMEOUT);
        return convertToWiningRecordsDTO(winningRecordDOList);
    }

    private List<WiningRecordsDTO> convertToWiningRecordsDTO(List<WinningRecordDO> winningRecordDOList) {
        if(CollectionUtils.isEmpty(winningRecordDOList)) {
            return Arrays.asList();
        }
        List<WiningRecordsDTO> winingRecordsDTOList = winningRecordDOList
                .stream()
                .map(
                        winningRecordDO -> {
                            WiningRecordsDTO winingRecordsDTO = new WiningRecordsDTO();
                            winingRecordsDTO.setWinnerId(winningRecordDO.getWinnerId());
                            winingRecordsDTO.setWinnerName(winningRecordDO.getWinnerName());
                            winingRecordsDTO.setPrizeName(winningRecordDO.getPrizeName());
                            winingRecordsDTO.setPrizeTier(
                                    ActivityPrizeTiersEnum.forName(winningRecordDO.getPrizeTier()));
                            winingRecordsDTO.setWinningTime(winningRecordDO.getWinningTime());
                            return winingRecordsDTO;
                        }
                ).collect(Collectors.toList());
        return winingRecordsDTOList;
    }

    //从缓存中删除
    private void deleteWinningRecords(String key) {
        try {
            if(redisUtil.hasKey(WINNING_RECOREDS_PREFIX+key)){
                redisUtil.del(WINNING_RECOREDS_PREFIX+key);
            }
        }catch (Exception e) {
            log.error("从缓存删除中奖记录异常,key:{}",key);
        }

    }

    /*从缓存中放入中奖记录*/
    private void cacheWinningRecords(String key,
                                     List<WinningRecordDO> winningRecordDOList,
                                     Long time) {
        String str = null;
        try {
            if(!StringUtils.hasText(key)
                    || CollectionUtils.isEmpty(winningRecordDOList)) {
                log.warn("要缓存的内容为空！key:{},value{}",key,str);
                return;
            }

            str = JacksonUtil.writeValueAsString(winningRecordDOList);
            redisUtil.set(WINNING_RECOREDS_PREFIX+ key,str,time);
        }catch (Exception e) {
            log.error("缓存中奖记录异常! key:{},value:{}",WINNING_RECOREDS_PREFIX+ key,str);
        }
    }
    /*从缓存中获取中奖记录*/
    private List<WinningRecordDO> getWinningRrecords(String key) {
        try {
            if(!StringUtils.hasText(key)) {
                log.warn("缓存中要查询的中奖记录的key为空");
                return Arrays.asList();
            }
            String str = redisUtil.get(WINNING_RECOREDS_PREFIX+key);
            if(!StringUtils.hasText(str)) {
                return Arrays.asList();
            }
            return JacksonUtil.readListValue(str, WinningRecordDO.class);
        }catch (Exception e) {
            log.error("从缓存中查询中奖记录异常！key{}",WINNING_RECOREDS_PREFIX+ key);
            return Arrays.asList();
        }
    }
}
